package com.strange.mdl.module.mdl.controller.admin.oss;

import com.aliyun.oss.model.PartETag;
import com.strange.mdl.framework.common.pojo.CommonResult;
import com.strange.mdl.module.mdl.utils.OssUtil;
import jakarta.annotation.security.PermitAll;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;
import java.util.concurrent.*;

import static com.strange.mdl.framework.common.pojo.CommonResult.success;

@RestController
@Slf4j
@RequestMapping("/mdl/part/upload")
public class FileUploadController {

    @Autowired
    private OssUtil ossUtil;

    private final Map<String, ExecutorService> executorServiceMap = new ConcurrentHashMap<>();


    // 存储每个上传任务的partETags
    private final static Map<String, List<PartETag>> partETagsMap = new ConcurrentHashMap<>();
    private final static Map<String, Set<Integer>> partETagsPartNumberMap = new ConcurrentHashMap<>();
    private final static Map<String, Set<Integer>> partPartFailNumberMap = new ConcurrentHashMap<>();

    @GetMapping("/init")
    @PermitAll
    public CommonResult<String> initiateUpload(@RequestParam("objectName") String name) {

        int corePoolSize = Runtime.getRuntime().availableProcessors() * 2; // 根据 CPU 动态调整
        ExecutorService executorService = new ThreadPoolExecutor(
                corePoolSize,
                corePoolSize + 4,
                60L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(200),
                new ThreadPoolExecutor.CallerRunsPolicy());
        // 存储每个 uploadId 对应的线程池

        String uploadId = ossUtil.initiateMultipartUpload(name);
        // 支持多任务上传，每个任务有独立的线程池
        executorServiceMap.put(uploadId, executorService);

        partETagsMap.put(uploadId, new ArrayList<>());
        partETagsPartNumberMap.put(uploadId, Collections.newSetFromMap(new ConcurrentHashMap<>()));
        partPartFailNumberMap.put(uploadId, Collections.newSetFromMap(new ConcurrentHashMap<>()));
        return success(uploadId);
    }


    @PostMapping("/syncpart")
    @PermitAll
    public CommonResult<String> uploadPart(
            @RequestParam("uploadId") String uploadId,
            @RequestParam("objectName") String objectName,
            @RequestParam("groupNumber") Integer groupNumber,
            @RequestParam("partNumberGroup") Integer partNumberGroup, // 第几组 0, 1, 2
            @RequestParam("files") List<MultipartFile> files) {
        int size = files.size();
        CountDownLatch latch = new CountDownLatch(size);
        for (int i = 0; i < size; i++) {
            int partNumber = partNumberGroup * groupNumber + i + 1;
            MultipartFile file = files.get(i);
            ExecutorService executorService = executorServiceMap.get(uploadId);
            executorService.submit(() -> {
                //  忽略重复上传
                if (partETagsPartNumberMap.get(uploadId).contains(partNumber)) {
                    latch.countDown();
                    return;
                }
                try {
                    byte[] bytes = file.getBytes();
                    log.info("上传分片partNumber {}", partNumber);
                    PartETag partETag = ossUtil.uploadPart(objectName, partNumber, uploadId, bytes);
                    log.info("上传分片成功partNumber {}", partNumber);
                    // 记录成功后上传的partETag
                    partETagsMap.get(uploadId).add(partETag);
                    // 记录成功后上传的partNumber
                    partETagsPartNumberMap.get(uploadId).add(partNumber);
                } catch (Exception e) {
                    log.error("上传分片 partNumber {} 失败", partNumber, e);
                    partPartFailNumberMap.get(uploadId).add(partNumber);
                } finally {
                    latch.countDown();
                }
            });
        }

        // 等待所有线程完成
        try {
            latch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        log.info("第 {} 组上传完成", partNumberGroup);
        return success("ok");
//        Map<String, Integer> stringIntegerHashMap = new HashMap<>();
//        stringIntegerHashMap.put("partNumberGroup", partNumberGroup);
//        stringIntegerHashMap.put("size", size);
//        return new Result<Map<String, Integer>>().ok(stringIntegerHashMap);
    }


    @PostMapping("/part")
    @PermitAll
    public CommonResult<String> uploadPart(
            @RequestParam("uploadId") String uploadId,
            @RequestParam("objectName") String objectName,
            @RequestParam("partNumber") Integer partNumber,
            @RequestParam("file") MultipartFile file) {

        if (file.isEmpty()) {
            return success("error");
        }

        try {
            byte[] bytes = file.getBytes();
            PartETag partETag = ossUtil.uploadPart(objectName, partNumber, uploadId, bytes);
            partETagsMap.get(uploadId).add(partETag);
            return success("Part uploaded successfully.");
        } catch (Exception e) {
            partPartFailNumberMap.get(uploadId).add(partNumber);
            return success("Failed to upload part " + partNumber + ": " + e.getMessage());
        }


    }

    @PostMapping("/checkPart")
    @PermitAll
    public CommonResult<Set<Integer>> checkPart(@RequestParam("uploadId") String uploadId) {
        Set<Integer> integers = partPartFailNumberMap.get(uploadId);
        return success(integers);
    }

    @PostMapping("/complete")
    @PermitAll
    public CommonResult<String> completeUpload(
            @RequestParam("uploadId") String uploadId,
            @RequestParam("objectName") String objectName) {

        List<PartETag> partETags = partETagsMap.get(uploadId);
        if (partETags == null || partETags.isEmpty()) {
            return success("No parts uploaded for this upload id.");
        }

        String location = ossUtil.completeMultipartUpload(objectName, uploadId, partETags);
        // 清理资源
        partETagsMap.remove(uploadId);
        partETagsPartNumberMap.remove(uploadId);
        partPartFailNumberMap.remove(uploadId);

        ExecutorService executorService = executorServiceMap.get(uploadId);
        executorService.shutdown();
        executorServiceMap.remove(uploadId);
        return success(location);
    }
}